package com.aim.config;

import java.io.IOException;
import java.sql.SQLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;

import com.alibaba.druid.pool.DruidDataSource;

/**
 * @AUTO 阿里数据库配置类
 * @FILE DruidConfig.java
 * @DATE 2021年4月25日 上午9:44:32
 * @Author AIM
 * @Version
 */
@Configuration
@PropertySource("classpath:jdbc.properties")
@MapperScan(basePackages = "com.aim.dao")
public class DruidConfig {

	@Value("${jdbc.driver}")
	private String driverClassName;

	@Value("${jdbc.url}")
	private String dbUrl;

	@Value("${jdbc.username}")
	private String username;

	@Value("${jdbc.password}")
	private String password;

	@Value("${jdbc.initialSize}")
	private int initialSize;

	@Value("${jdbc.maxActive}")
	private int maxActive;

	@Value("${jdbc.minIdle}")
	private int minIdle;

	@Value("${jdbc.maxWait}")
	private int maxWait;

	@Value("${jdbc.timeBetweenEvictionRunsMillis}")
	private int timeBetweenEvictionRunsMillis;

	@Value("${jdbc.minEvictableIdleTimeMillis}")
	private int minEvictableIdleTimeMillis;

	@Value("${jdbc.validationQuery}")
	private String validationQuery;

	@Value("${jdbc.testWhileIdle}")
	private boolean testWhileIdle;

	@Value("${jdbc.testOnBorrow}")
	private boolean testOnBorrow;

	@Value("${jdbc.testOnReturn}")
	private boolean testOnReturn;

	@Value("${jdbc.poolPreparedStatements}")
	private boolean poolPreparedStatements;

	@Value("${jdbc.maxPoolPreparedStatementPerConnectionSize}")
	private int maxPoolPreparedStatementPerConnectionSize;

	@Value("${jdbc.filters}")
	private String filters;

	@Value("{jdbc.connectionProperties}")
	private String connectionProperties;

	// 声明其为Bean实例
	@Bean
	public DataSource dataSource() {
		DruidDataSource datasource = new DruidDataSource();
		datasource.setUrl(dbUrl);
		datasource.setUsername(username);
		datasource.setPassword(password);
		datasource.setDriverClassName(driverClassName);
		datasource.setInitialSize(initialSize);
		datasource.setMinIdle(minIdle);
		datasource.setMaxActive(maxActive);
		datasource.setMaxWait(maxWait);
		datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
		datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
		datasource.setValidationQuery(validationQuery);
		datasource.setTestWhileIdle(testWhileIdle);
		datasource.setTestOnBorrow(testOnBorrow);
		datasource.setTestOnReturn(testOnReturn);
		datasource.setPoolPreparedStatements(poolPreparedStatements);
		datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
		try {
			datasource.setFilters(filters);
		} catch (SQLException e) {
		}
		datasource.setConnectionProperties(connectionProperties);
		return datasource;
	}

	// MYBATIS的配置
	@Bean
	public SqlSessionFactoryBean sqlSessionFactoryBean() throws IOException {
		ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
		SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
		sqlSessionFactoryBean.setDataSource(dataSource());// 数据源
		sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath:com/aim/mapper/*.xml"));
		sqlSessionFactoryBean.setTypeAliasesPackage("com.aim.bean");// 别名，让*Mpper.xml实体类映射可以不加上具体包名
		return sqlSessionFactoryBean;
	}

	@Bean(name = "jdbcTemplate")
	public JdbcTemplate jdbcTemplate() {
		return new JdbcTemplate(dataSource());
	}

	@Bean(name = "transactionManager")
	public DataSourceTransactionManager transactionManager() {
		DataSourceTransactionManager manager = new DataSourceTransactionManager();
		manager.setDataSource(dataSource());
		return manager;
	}

	/**
	 * 事务配置
	 */
	@Bean(name = "txAdvice")
	public TransactionInterceptor txAdvice(@Qualifier("transactionManager") DataSourceTransactionManager dstm) {
		/* 只读事务，不做更新操作 */
		RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
		readOnlyTx.setReadOnly(true);
		readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);

		/* 当前存在事务就使用当前事务，当前不存在事务就创建一个新的事务 */
		RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();
		requiredTx.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
		requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
		/* requiredTx.setTimeout(txMethodTimeout); */
		Map<String, TransactionAttribute> txMap = new HashMap<String, TransactionAttribute>();
		txMap.put("add*", requiredTx);
		txMap.put("create*", requiredTx);
		txMap.put("insert*", requiredTx);
		txMap.put("modify*", requiredTx);
		txMap.put("save*", requiredTx);
		txMap.put("update*", requiredTx);
		txMap.put("edit*", requiredTx);
		txMap.put("delete*", requiredTx);
		txMap.put("remove*", requiredTx);
		txMap.put("repair", requiredTx);
		txMap.put("delAndRepair", requiredTx);
		txMap.put("get*", readOnlyTx);
		txMap.put("find*", readOnlyTx);
		txMap.put("load*", readOnlyTx);
		txMap.put("query*", readOnlyTx);
		NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
		source.setNameMap(txMap);
		return new TransactionInterceptor(dstm, source);
	}

	/**
	 * 利用AspectJExpressionPointcut设置切点
	 */
	@Bean(name = "txPointcut")
	public AspectJExpressionPointcut txPointcut() {
		AspectJExpressionPointcut txPointcut = new AspectJExpressionPointcut();
		txPointcut.setExpression("execution(* com.aim.service.*.*(..))");
		return txPointcut;
	}
}
